/* -------------------------------------------------------------------------------

This code is based on source provided under the terms of the Id Software 
LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the
GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of 
LICENSE_ID, please contact Id Software immediately at info@idsoftware.com.

All changes and additions to the original source which have been developed by
other contributors (see CONTRIBUTORS) are provided under the terms of the
license the contributors choose (see LICENSE), to the extent permitted by the
LICENSE_ID. If you did not receive a copy of the contributor license,
please contact the GtkRadiant maintainers at info@gtkradiant.com immediately.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

----------------------------------------------------------------------------------

This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."

------------------------------------------------------------------------------- */



/* marker */
#define LIGHT_C



/* dependencies */
#include "q3map2.h"



/*
CreateSunLight() - ydnar
this creates a sun light
*/

static void CreateSunLight( sun_t *sun )
{
	int			i;
	float		photons, d, angle, elevation, da, de;
	vec3_t		direction;
	light_t		*light;
	
	
	/* dummy check */
	if( sun == NULL )
		return;
	
	/* fixup */
	if( sun->numSamples < 1 )
		sun->numSamples = 1;
	
	/* set photons */
	photons = sun->photons / sun->numSamples;
	
	/* create the right number of suns */
	for( i = 0; i < sun->numSamples; i++ )
	{
		/* calculate sun direction */
		if( i == 0 )
			VectorCopy( sun->direction, direction );
		else
		{
			/*
				sun->direction[ 0 ] = cos( angle ) * cos( elevation );
				sun->direction[ 1 ] = sin( angle ) * cos( elevation );
				sun->direction[ 2 ] = sin( elevation );
				
				xz_dist   = sqrt( x*x + z*z )
				latitude  = atan2( xz_dist, y ) * RADIANS
				longitude = atan2( x,       z ) * RADIANS
			*/
			
			d = sqrt( sun->direction[ 0 ] * sun->direction[ 0 ] + sun->direction[ 1 ] * sun->direction[ 1 ] );
			angle = atan2( sun->direction[ 1 ], sun->direction[ 0 ] );
			elevation = atan2( sun->direction[ 2 ], d );
			
			/* jitter the angles (loop to keep random sample within sun->deviance steridians) */
			do
			{
				da = (Random() * 2.0f - 1.0f) * sun->deviance;
				de = (Random() * 2.0f - 1.0f) * sun->deviance;
			}
			while( (da * da + de * de) > (sun->deviance * sun->deviance) );
			angle += da;
			elevation += de;
			
			/* debug code */
			//%	Sys_Printf( "%d: Angle: %3.4f Elevation: %3.3f\n", sun->numSamples, (angle / Q_PI * 180.0f), (elevation / Q_PI * 180.0f) );
			
			/* create new vector */
			direction[ 0 ] = cos( angle ) * cos( elevation );
			direction[ 1 ] = sin( angle ) * cos( elevation );
			direction[ 2 ] = sin( elevation );
		}
		
		/* create a light */
		numSunLights++;
		light = safe_malloc( sizeof( *light ) );
		memset( light, 0, sizeof( *light ) );
		light->next = lights;
		lights = light;
		
		/* initialize the light */
		light->flags = LIGHT_SUN_DEFAULT;
		light->type = EMIT_SUN;
		light->fade = 1.0f;
		light->falloffTolerance = falloffTolerance;
		light->filterRadius = sun->filterRadius / sun->numSamples;
		light->style = sun->style;
		
		/* set the light's position out to infinity */
		VectorMA( vec3_origin, (MAX_WORLD_COORD * 8.0f), direction, light->origin );	/* MAX_WORLD_COORD * 2.0f */
		
		/* set the facing to be the inverse of the sun direction */
		VectorScale( direction, -1.0, light->normal );
		light->dist = DotProduct( light->origin, light->normal );
		
		/* set color and photons */
		VectorCopy( sun->color, light->color );
		light->photons = photons * skyScale;
	}

	/* another sun? */
	if( sun->next != NULL )
		CreateSunLight( sun->next );
}



/*
CreateSkyLights() - ydnar
simulates sky light with multiple suns
*/

static void CreateSkyLights( vec3_t color, float value, int iterations, float filterRadius, int style )
{
	int			i, j, numSuns;
	int			angleSteps, elevationSteps;
	float		angle, elevation;
	float		angleStep, elevationStep;
	float		step, start;
	sun_t		sun;
	
	
	/* dummy check */
	if( value <= 0.0f || iterations < 2 )
		return;
	
	/* calculate some stuff */
	step = 2.0f / (iterations - 1);
	start = -1.0f;
	
	/* basic sun setup */
	VectorCopy( color, sun.color );
	sun.deviance = 0.0f;
	sun.filterRadius = filterRadius;
	sun.numSamples = 1;
	sun.style = style;
	sun.next = NULL;
	
	/* setup */
	elevationSteps = iterations - 1;
	angleSteps = elevationSteps * 4;
	angle = 0.0f;
	elevationStep = DEG2RAD( 90.0f / iterations );	/* skip elevation 0 */
	angleStep = DEG2RAD( 360.0f / angleSteps );
	
	/* calc individual sun brightness */
	numSuns = angleSteps * elevationSteps + 1;
	sun.photons = value / numSuns;
	
	/* iterate elevation */
	elevation = elevationStep * 0.5f;
	angle = 0.0f;
	for( i = 0, elevation = elevationStep * 0.5f; i < elevationSteps; i++ )
	{
		/* iterate angle */
		for( j = 0; j < angleSteps; j++ )
		{
			/* create sun */
			sun.direction[ 0 ] = cos( angle ) * cos( elevation );
			sun.direction[ 1 ] = sin( angle ) * cos( elevation );
			sun.direction[ 2 ] = sin( elevation );
			CreateSunLight( &sun );
			
			/* move */
			angle += angleStep;
		}
			
		/* move */
		elevation += elevationStep;
		angle += angleStep / elevationSteps;
	}
	
	/* create vertical sun */
	VectorSet( sun.direction, 0.0f, 0.0f, 1.0f );
	CreateSunLight( &sun );
	
	/* short circuit */
	return;
}



/*
CreateEntityLights()
creates lights from light entities
*/

void CreateEntityLights( void )
{
	int				i, j;
	light_t			*light, *light2;
	entity_t		*e, *e2;
	const char		*name;
	const char		*target;
	vec3_t			dest;
	const char		*_color;
	float			intensity, scale, deviance, filterRadius;
	int				spawnflags, flags, numSamples;
	qboolean		junior;

	numPointLights=0;
	numSpotLights=0;
	
	/* go throught entity list and find lights */
	for( i = 0; i < numEntities; i++ )
	{
		/* get entity */
		e = &entities[ i ];
		name = ValueForKey( e, "classname" );
		
		/* ydnar: check for lightJunior */
		if( Q_strncasecmp( name, "lightJunior", 11 ) == 0 )
			junior = qtrue;
		else if( Q_strncasecmp( name, "light", 5 ) == 0 )
			junior = qfalse;
		else
			continue;
		
		/* lights with target names (and therefore styles) are only parsed from BSP */
		target = ValueForKey( e, "targetname" );
		if( target[ 0 ] != '\0' && i >= numBSPEntities )
			continue;
		
		/* create a light */
		numPointLights++;
		light = safe_malloc( sizeof( *light ) );
		memset( light, 0, sizeof( *light ) );
		light->next = lights;
		lights = light;
		
		/* handle spawnflags */
		spawnflags = IntForKey( e, "spawnflags" );
		
		/* ydnar: quake 3+ light behavior */
		if( wolfLight == qfalse )
		{
			/* set default flags */
			flags = LIGHT_Q3A_DEFAULT;
			
			/* linear attenuation? */
			if( spawnflags & 1 )
			{
				flags |= LIGHT_ATTEN_LINEAR;
				flags &= ~LIGHT_ATTEN_ANGLE;
			}
			
			/* no angle attenuate? */
			if( spawnflags & 2 )
				flags &= ~LIGHT_ATTEN_ANGLE;
		}
		
		/* ydnar: wolf light behavior */
		else
		{
			/* set default flags */
			flags = LIGHT_WOLF_DEFAULT;
			
			/* inverse distance squared attenuation? */
			if( spawnflags & 1 )
			{
				flags &= ~LIGHT_ATTEN_LINEAR;
				flags |= LIGHT_ATTEN_ANGLE;
			}
			
			/* angle attenuate? */
			if( spawnflags & 2 )
				flags |= LIGHT_ATTEN_ANGLE;
		}
		
		/* other flags (borrowed from wolf) */
		
		/* wolf dark light? */
		if( (spawnflags & 4) || (spawnflags & 8) )
			flags |= LIGHT_DARK;
		
		/* nogrid? */
		if( spawnflags & 16 )
			flags &= ~LIGHT_GRID;
		
		/* junior? */
		if( junior )
		{
			flags |= LIGHT_GRID;
			flags &= ~LIGHT_SURFACES;
		}
		
		/* store the flags */
		light->flags = flags;
		
		/* ydnar: set fade key (from wolf) */
		light->fade = 1.0f;
		if( light->flags & LIGHT_ATTEN_LINEAR )
		{
			light->fade = FloatForKey( e, "fade" );
			if( light->fade == 0.0f )
				light->fade = 1.0f;
		}
		
		/* ydnar: set angle scaling (from vlight) */
		light->angleScale = FloatForKey( e, "_anglescale" );
		if( light->angleScale != 0.0f )
			light->flags |= LIGHT_ATTEN_ANGLE;
		
		/* set origin */
		GetVectorForKey( e, "origin", light->origin);
		light->style = IntForKey( e, "_style" );
		if( light->style == 0 )
			light->style = IntForKey( e, "style" );
		if( light->style < LS_NORMAL || light->style >= LS_NONE )
			Error( "Invalid lightstyle (%d) on entity %d", light->style, i );
		
		/* set light intensity */
		intensity = FloatForKey( e, "_light" );
		if( intensity == 0.0f )
			intensity = FloatForKey( e, "light" );
		if( intensity == 0.0f)
			intensity = 300.0f;
		
		/* ydnar: set light scale (sof2) */
		scale = FloatForKey( e, "scale" );
		if( scale == 0.0f )
			scale = 1.0f;
		intensity *= scale;
		
		/* ydnar: get deviance and samples */
		deviance = FloatForKey( e, "_deviance" );
		if( deviance == 0.0f )
			deviance = FloatForKey( e, "_deviation" );
		if( deviance == 0.0f )
			deviance = FloatForKey( e, "_jitter" );
		numSamples = IntForKey( e, "_samples" );
		if( deviance < 0.0f || numSamples < 1 )
		{
			deviance = 0.0f;
			numSamples = 1;
		}
		intensity /= numSamples;
		
		/* ydnar: get filter radius */
		filterRadius = FloatForKey( e, "_filterradius" );
		if( filterRadius == 0.0f )
			filterRadius = FloatForKey( e, "_filteradius" );
		if( filterRadius == 0.0f )
			filterRadius = FloatForKey( e, "_filter" );
		if( filterRadius < 0.0f )
			filterRadius = 0.0f;
		light->filterRadius = filterRadius;
		
		/* set light color */
		_color = ValueForKey( e, "_color" );
		if( _color && _color[ 0 ] )
		{
			sscanf( _color, "%f %f %f", &light->color[ 0 ], &light->color[ 1 ], &light->color[ 2 ] );
			ColorNormalize( light->color, light->color );
		}
		else
			light->color[ 0 ] = light->color[ 1 ] = light->color[ 2 ] = 1.0f;
		
		intensity = intensity * pointScale;
		light->photons = intensity;
		
		light->type = EMIT_POINT;
		
		/* set falloff threshold */
		light->falloffTolerance = falloffTolerance / numSamples;
		
		/* lights with a target will be spotlights */
		target = ValueForKey( e, "target" );
		if( target[ 0 ] )
		{
			float		radius;
			float		dist;
			sun_t		sun;
			const char	*_sun;
			
			
			/* get target */
			e2 = FindTargetEntity( target );
			if( e2 == NULL )
			{
				Sys_Printf( "WARNING: light at (%i %i %i) has missing target\n",
					(int) light->origin[ 0 ], (int) light->origin[ 1 ], (int) light->origin[ 2 ] );
			}
			else
			{
				/* not a point light */
				numPointLights--;
				numSpotLights++;
				
				/* make a spotlight */
				GetVectorForKey( e2, "origin", dest );
				VectorSubtract( dest, light->origin, light->normal );
				dist = VectorNormalize( light->normal, light->normal );
				radius = FloatForKey( e, "radius" );
				if( !radius )
					radius = 64;
				if( !dist )
					dist = 64;
				light->radiusByDist = (radius + 16) / dist;
				light->type = EMIT_SPOT;
				
				/* ydnar: wolf mods: spotlights always use nonlinear + angle attenuation */
				light->flags &= ~LIGHT_ATTEN_LINEAR;
				light->flags |= LIGHT_ATTEN_ANGLE;
				light->fade = 1.0f;
				
				/* ydnar: is this a sun? */
				_sun = ValueForKey( e, "_sun" );
				if( _sun[ 0 ] == '1' )
				{
					/* not a spot light */
					numSpotLights--;
					
					/* unlink this light */
					lights = light->next;
					
					/* make a sun */
					VectorScale( light->normal, -1.0f, sun.direction );
					VectorCopy( light->color, sun.color );
					sun.photons = (intensity / pointScale);
					sun.deviance = deviance / 180.0f * Q_PI;
					sun.numSamples = numSamples;
					sun.style = light->style;
					sun.next = NULL;
					
					/* make a sun light */
					CreateSunLight( &sun );
					
					/* free original light */
					free( light );
					light = NULL;
					
					/* skip the rest of this love story */
					continue;
				}
			}
		}
		
		/* jitter the light */
		for( j = 1; j < numSamples; j++ )
		{
			/* create a light */
			light2 = safe_malloc( sizeof( *light ) );
			memcpy( light2, light, sizeof( *light ) );
			light2->next = lights;
			lights = light2;
			
			/* add to counts */
			if( light->type == EMIT_SPOT )
				numSpotLights++;
			else
				numPointLights++;
			
			/* jitter it */
			light2->origin[ 0 ] = light->origin[ 0 ] + (Random() * 2.0f - 1.0f) * deviance;
			light2->origin[ 1 ] = light->origin[ 1 ] + (Random() * 2.0f - 1.0f) * deviance;
			light2->origin[ 2 ] = light->origin[ 2 ] + (Random() * 2.0f - 1.0f) * deviance;
		}
	}
}



/*
CreateSurfaceLights() - ydnar
this hijacks the radiosity code to generate surface lights for first pass
*/

#define APPROX_BOUNCE	1.0f

void CreateSurfaceLights( void )
{
	int					i;
	bspDrawSurface_t	*ds;
	surfaceInfo_t		*info;
	shaderInfo_t		*si;
	light_t				*light;
	float				subdivide;
	vec3_t				origin;
	clipWork_t			cw;
	const char			*nss;
	
	
	/* get sun shader supressor */
	nss = ValueForKey( &entities[ 0 ], "_noshadersun" );
	

	/* Reset the sun/skylights if we're radbumping */
	for( i = 0; i < numBSPDrawSurfaces; i++ )
	{
		ds = &bspDrawSurfaces[ i ];
		info = &surfaceInfos[ i ];
		si = info->si;

		si->skyLight_done=0;
		si->sun_done=0;
	}

	/* walk the list of surfaces */
	for( i = 0; i < numBSPDrawSurfaces; i++ )
	{
		/* get surface and other bits */
		ds = &bspDrawSurfaces[ i ];
		info = &surfaceInfos[ i ];

		si = info->si;
		
		/* sunlight? */
		if( si->sun != NULL && nss[ 0 ] != '1' && si->sun_done==0)
		{
			Sys_FPrintf( SYS_VRB, "Sun: %s\n", si->shader );
			CreateSunLight( si->sun );
			//si->sun = NULL; /* FIXME: leak! */
			si->sun_done=1;
		}
		
		/* sky light? */
		if( si->skyLightValue > 0.0f && si->skyLight_done==0)
		{
			Sys_FPrintf( SYS_VRB, "Sky: %s\n", si->shader );
			CreateSkyLights( si->color, si->skyLightValue, si->skyLightIterations, si->lightFilterRadius, si->lightStyle );
			//si->skyLightValue = 0.0f;	/* FIXME: hack! */
			si->skyLight_done=1;
		}
		
		/* try to early out */
		if( si->value <= 0 )
			continue;
		
		/* autosprite shaders become point lights */
		if( si->autosprite )
		{
			/* create an average xyz */
			VectorAdd( info->mins, info->maxs, origin );
			VectorScale( origin, 0.5f, origin );
			
			/* create a light */
			light = safe_malloc( sizeof( *light ) );
			memset( light, 0, sizeof( *light ) );
			light->next = lights;
			lights = light;
			
			/* set it up */
			light->flags = LIGHT_Q3A_DEFAULT;
			light->type = EMIT_POINT;
			light->photons = si->value * pointScale;
			light->fade = 1.0f;
			light->si = si;
			VectorCopy( origin, light->origin );
			VectorCopy( si->color, light->color );
			light->falloffTolerance = falloffTolerance;
			light->style = light->style;
			
			/* add to point light count and continue */
			numPointLights++;
			continue;
		}
		
		/* get subdivision amount */
		if( si->lightSubdivide > 0 )
			subdivide = si->lightSubdivide;
		else
			subdivide = defaultLightSubdivide;
		
		/* switch on type */
		switch( ds->surfaceType )
		{
			case MST_PLANAR:
			case MST_TRIANGLE_SOUP:
				RadLightForTriangles( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );
				break;
			
			case MST_PATCH:
				RadLightForPatch( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );
				break;
			
			default:
				break;
		}
	}
}



/*
SetEntityOrigins()
find the offset values for inline models
*/

void SetEntityOrigins( void )
{
	int					i, j, k, f;
	entity_t			*e;
	vec3_t				origin;
	const char			*key;
	int					modelnum;
	bspModel_t			*dm;
	bspDrawSurface_t	*ds;
	
	
	/* ydnar: copy drawverts into private storage for nefarious purposes */
	yDrawVerts = safe_malloc( numBSPDrawVerts * sizeof( bspDrawVert_t ) );
	memcpy( yDrawVerts, bspDrawVerts, numBSPDrawVerts * sizeof( bspDrawVert_t ) );
	
	/* set the entity origins */
	for( i = 0; i < numEntities; i++ )
	{
		/* get entity and model */
		e = &entities[ i ];
		key = ValueForKey( e, "model" );
		if( key[ 0 ] != '*' )
			continue;
		modelnum = atoi( key + 1 );
		dm = &bspModels[ modelnum ];
		
		/* get entity origin */
		key = ValueForKey( e, "origin" );
		if( key[ 0 ] == '\0' )
			continue;
		GetVectorForKey( e, "origin", origin );
		
		/* set origin for all surfaces for this model */
		for( j = 0; j < dm->numBSPSurfaces; j++ )
		{
			/* get drawsurf */
			ds = &bspDrawSurfaces[ dm->firstBSPSurface + j ];
			
			/* set its verts */
			for( k = 0; k < ds->numVerts; k++ )
			{
				f = ds->firstVert + k;
				VectorAdd( origin, bspDrawVerts[ f ].xyz, yDrawVerts[ f ].xyz );
			}
		}
	}
}



/*
PointToPolygonFormFactor()
calculates the area over a point/normal hemisphere a winding covers
ydnar: fixme: there has to be a faster way to calculate this
without the expensive per-vert sqrts and transcendental functions
ydnar 2002-09-30: added -faster switch because only 19% deviance > 10%
between this and the approximation
*/

#define ONE_OVER_2PI	0.159154942f	//% (1.0f / (2.0f * 3.141592657f))

float PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w )
{
	vec3_t		triVector, triNormal;
	int			i, j;
	vec3_t		dirs[ MAX_POINTS_ON_WINDING ];
	float		total;
	float		dot, angle, facing;
	
	
	/* this is expensive */
	for( i = 0; i < w->numpoints; i++ )
	{
		VectorSubtract( w->p[ i ], point, dirs[ i ] );
		VectorNormalize( dirs[ i ], dirs[ i ] );
	}
	
	/* duplicate first vertex to avoid mod operation */
	VectorCopy( dirs[ 0 ], dirs[ i ] );
	
	/* calculcate relative area */
	total = 0.0f;
	for( i = 0; i < w->numpoints; i++ )
	{
		/* get a triangle */
		j = i + 1;
		dot = DotProduct( dirs[ i ], dirs[ j ] );
		
		/* roundoff can cause slight creep, which gives an IND from acos */
		if( dot > 1.0f )
			dot = 1.0f;
		else if( dot < -1.0f )
			dot = -1.0f;
		
		/* get the angle */
		angle = acos( dot );
		
		CrossProduct( dirs[ i ], dirs[ j ], triVector );
		if( VectorNormalize( triVector, triNormal ) < 0.0001f )
			continue;
		
		facing = DotProduct( normal, triNormal );
		total += facing * angle;
		
		/* ydnar: this was throwing too many errors with radiosity + crappy maps. ignoring it. */
		if( total > 6.3f || total < -6.3f )
			return 0.0f;
	}
	
	/* now in the range of 0 to 1 over the entire incoming hemisphere */
	//%	total /= (2.0f * 3.141592657f);
	total *= ONE_OVER_2PI;
	return total;
}



/*
LightContributionTosample()
determines the amount of light reaching a sample (luxel or vertex) from a given light
*/

int LightContributionToSample( trace_t *trace )
{
	light_t			*light;
	float			angle;
	float			add;
	float			dist;
	
	
	/* get light */
	light = trace->light;
	
	/* clear color */
	VectorClear( trace->color );
	
	/* ydnar: early out */
	if( !(light->flags & LIGHT_SURFACES) || light->envelope <= 0.0f )
		return 0;
	
	/* do some culling checks */
	if( light->type != EMIT_SUN )
	{
		/* MrE: if the light is behind the surface */
		if( trace->twoSided == qfalse )
			if( DotProduct( light->origin, trace->normal ) - DotProduct( trace->origin, trace->normal ) < 0.0f )
				return 0;
		
		/* ydnar: test pvs */
		if( !ClusterVisible( trace->cluster, light->cluster ) )
			return 0;
	}
	
	/* ptpff approximation */
	if( light->type == EMIT_AREA && faster )
	{
		/* get direction and distance */
		VectorCopy( light->origin, trace->end );
		dist = SetupTrace( trace );
		if( dist >= light->envelope )
			return 0;
		
		/* clamp the distance to prevent super hot spots */
		if( dist < 16.0f )
			dist = 16.0f;
		
		/* angle attenuation */
		angle = DotProduct( trace->normal, trace->direction );
		
		/* twosided lighting */
		if( trace->twoSided )
			angle = fabs( angle );
		
		/* attenuate */
		angle *= -DotProduct( light->normal, trace->direction );
		if( angle <= 0.0f )
			return 0;
		add = light->photons / (dist * dist) * angle;
	}
	
	/* exact point to polygon form factor */
	else if( light->type == EMIT_AREA )
	{
		float		factor;
		float		d;
		vec3_t		pushedOrigin;
		
		
		/* project sample point into light plane */
		d = DotProduct( trace->origin, light->normal ) - light->dist;
		//%	if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )
		//%		return 0;
		if( d < 3.0f )
		{
			/* sample point behind plane? */
			if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )
				return 0;
			
			/* sample plane coincident? */
			if( d > -3.0f && DotProduct( trace->normal, light->normal ) > 0.9f )
				return 0;
		}
		
		/* nudge the point so that it is clearly forward of the light */
		/* so that surfaces meeting a light emiter don't get black edges */
		if( d > -8.0f && d < 8.0f )
			VectorMA( trace->origin, (8.0f - d), light->normal, pushedOrigin );				
		else
			VectorCopy( trace->origin, pushedOrigin );
		
		/* get direction and distance */
		VectorCopy( light->origin, trace->end );
		dist = SetupTrace( trace );
		if( dist >= light->envelope )
			return 0;
		
		/* calculate the contribution */
		factor = PointToPolygonFormFactor( pushedOrigin, trace->normal, light->w );
		if( factor == 0.0f )
			return 0;
		else if( factor < 0.0f )
		{
			/* twosided lighting */
			if( trace->twoSided || (light->flags & LIGHT_TWOSIDED) )
			{
				factor = -factor;

				/* push light origin to other side of the plane */
				VectorMA( light->origin, -2.0f, light->normal, trace->end );
				dist = SetupTrace( trace );
				if( dist >= light->envelope )
					return 0;
			}
			else
				return 0;
		}
		
		/* ydnar: moved to here */
		add = factor * light->add;
	}
	
	/* point/spot lights */
	else if( light->type == EMIT_POINT || light->type == EMIT_SPOT )
	{
		/* get direction and distance */
		VectorCopy( light->origin, trace->end );
		dist = SetupTrace( trace );
		if( dist >= light->envelope )
			return 0;
		
		/* clamp the distance to prevent super hot spots */
		if( dist < 16.0f )
			dist = 16.0f;
		
		/* angle attenuation */
		angle = (light->flags & LIGHT_ATTEN_ANGLE) ? DotProduct( trace->normal, trace->direction ) : 1.0f;
		if( light->angleScale != 0.0f )
		{
			angle /= light->angleScale;
			if( angle > 1.0f )
				angle = 1.0f;
		}
		
		/* twosided lighting */
		if( trace->twoSided )
			angle = fabs( angle );
		
		/* attenuate */
		if( light->flags & LIGHT_ATTEN_LINEAR )
		{
			add = angle * light->photons * linearScale - (dist * light->fade);
			if( add < 0.0f )
				add = 0.0f;
		}
		else
			add = light->photons / (dist * dist) * angle;
		
		/* handle spotlights */
		if( light->type == EMIT_SPOT )
		{
			float	distByNormal, radiusAtDist, sampleRadius;
			vec3_t	pointAtDist, distToSample;
			
			
			/* do cone calculation */
			distByNormal = -DotProduct( trace->displacement, light->normal );
			if( distByNormal < 0.0f )
				return 0;
			VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
			radiusAtDist = light->radiusByDist * distByNormal;
			VectorSubtract( trace->origin, pointAtDist, distToSample );
			sampleRadius = VectorLength( distToSample );
			
			/* outside the cone */
			if( sampleRadius >= radiusAtDist )
				return 0;
			
			/* attenuate */
			if( sampleRadius > (radiusAtDist - 32.0f) )
				add *= ((radiusAtDist - sampleRadius) / 32.0f);
		}
	}
	
	/* ydnar: sunlight */
	else if( light->type == EMIT_SUN )
	{
		/* get origin and direction */
		VectorAdd( trace->origin, light->origin, trace->end );
		dist = SetupTrace( trace );
		
		/* angle attenuation */
		angle = (light->flags & LIGHT_ATTEN_ANGLE)
			? DotProduct( trace->normal, trace->direction )
			: 1.0f;
		
		/* twosided lighting */
		if( trace->twoSided )
			angle = fabs( angle );
		
		/* attenuate */
		add = light->photons * angle;
		if( add <= 0.0f )
			return 0;
		
		/* setup trace */
		trace->testAll = qtrue;
		VectorScale( light->color, add, trace->color );
		
		/* trace to point */
		if( trace->testOcclusion && !trace->forceSunlight )
		{
			/* trace */
			TraceLine( trace );
			if( !(trace->compileFlags & C_SKY) || trace->opaque )
			{
				VectorClear( trace->color );
				return -1;
			}
		}
		
		/* return to sender */
		return 1;
	}
	
	/* ydnar: changed to a variable number */
	if( add <= 0.0f || (add <= light->falloffTolerance && (light->flags & LIGHT_FAST_ACTUAL)) )
		return 0;
	
	/* setup trace */
	trace->testAll = qfalse;
	VectorScale( light->color, add, trace->color );
	
	/* raytrace */
	TraceLine( trace );
	if( trace->passSolid || trace->opaque )
	{
		VectorClear( trace->color );
		return -1;
	}
	
	/* return to sender */
	return 1;
}



/*
LightingAtSample()
determines the amount of light reaching a sample (luxel or vertex)
*/

void LightingAtSample( trace_t *trace, byte styles[ MAX_LIGHTMAPS ], vec3_t colors[ MAX_LIGHTMAPS ] )
{
	int				i, lightmapNum;
	
	
	/* clear colors */
	for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
		VectorClear( colors[ lightmapNum ] );
	
	/* ydnar: normalmap */
	if( normalmap )
	{
		colors[ 0 ][ 0 ] = (trace->normal[ 0 ] + 1.0f) * 127.5f;
		colors[ 0 ][ 1 ] = (trace->normal[ 1 ] + 1.0f) * 127.5f;
		colors[ 0 ][ 2 ] = (trace->normal[ 2 ] + 1.0f) * 127.5f;
		return;
	}
	
	/* ydnar: don't bounce ambient all the time */
	if( !bouncing )
		VectorCopy( ambientColor, colors[ 0 ] );
	
	/* ydnar: trace to all the list of lights pre-stored in tw */
	for( i = 0; i < trace->numLights && trace->lights[ i ] != NULL; i++ )
	{
		/* set light */
		trace->light = trace->lights[ i ];
		
		/* style check */
		for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
		{
			if( styles[ lightmapNum ] == trace->light->style ||
				styles[ lightmapNum ] == LS_NONE )
				break;
		}
		
		/* max of MAX_LIGHTMAPS (4) styles allowed to hit a sample */
		if( lightmapNum >= MAX_LIGHTMAPS )
			continue;
		
		/* sample light */
		LightContributionToSample( trace );
		if( trace->color[ 0 ] == 0.0f && trace->color[ 1 ] == 0.0f && trace->color[ 2 ] == 0.0f )
			continue;
		
		/* handle negative light */
		if( trace->light->flags & LIGHT_NEGATIVE )
			VectorScale( trace->color, -1.0f, trace->color );
		
		/* set style */
		styles[ lightmapNum ] = trace->light->style;
		
		/* add it */
		VectorAdd( colors[ lightmapNum ], trace->color, colors[ lightmapNum ] );
		
		/* cheap mode */
		if( cheap &&
			colors[ 0 ][ 0 ] >= 255.0f &&
			colors[ 0 ][ 1 ] >= 255.0f &&
			colors[ 0 ][ 2 ] >= 255.0f )
			break;
	}
}



/*
LightContributionToPoint()
for a given light, how much light/color reaches a given point in space (with no facing)
note: this is similar to LightContributionToSample() but optimized for omnidirectional sampling
*/

int LightContributionToPoint( trace_t *trace )
{
	light_t		*light;
	float		add, dist;
	
	
	/* get light */
	light = trace->light;
	
	/* clear color */
	VectorClear( trace->color );
	
	/* ydnar: early out */
	if( !(light->flags & LIGHT_GRID) || light->envelope <= 0.0f )
		return qfalse;
	
	/* is this a sun? */
	if( light->type != EMIT_SUN )
	{
		/* sun only? */
		if( sunOnly )
			return qfalse;
		
		/* test pvs */
		if( !ClusterVisible( trace->cluster, light->cluster ) )
			return qfalse;
	}
	
	/* ydnar: check origin against light's pvs envelope */
	if( trace->origin[ 0 ] > light->maxs[ 0 ] || trace->origin[ 0 ] < light->mins[ 0 ] ||
		trace->origin[ 1 ] > light->maxs[ 1 ] || trace->origin[ 1 ] < light->mins[ 1 ] ||
		trace->origin[ 2 ] > light->maxs[ 2 ] || trace->origin[ 2 ] < light->mins[ 2 ] )
	{
		gridBoundsCulled++;
		return qfalse;
	}
	
	/* set light origin */
	if( light->type == EMIT_SUN )
		VectorAdd( trace->origin, light->origin, trace->end );
	else
		VectorCopy( light->origin, trace->end );
	
	/* set direction */
	dist = SetupTrace( trace );
	
	/* test envelope */
	if( dist > light->envelope )
	{
		gridEnvelopeCulled++;
		return qfalse;
	}
	
	/* ptpff approximation */
	if( light->type == EMIT_AREA && faster )
	{
		/* clamp the distance to prevent super hot spots */
		if( dist < 16.0f )
			dist = 16.0f;
		
		/* attenuate */
		add = light->photons / (dist * dist);
	}
	
	/* exact point to polygon form factor */
	else if( light->type == EMIT_AREA )
	{
		float		factor, d;
		vec3_t		pushedOrigin;
		
		
		/* see if the point is behind the light */
		d = DotProduct( trace->origin, light->normal ) - light->dist;
		if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )
			return qfalse;
		
		/* nudge the point so that it is clearly forward of the light */
		/* so that surfaces meeting a light emiter don't get black edges */
		if( d > -8.0f && d < 8.0f )
			VectorMA( trace->origin, (8.0f - d), light->normal, pushedOrigin );				
		else
			VectorCopy( trace->origin, pushedOrigin );
		
		/* calculate the contribution (ydnar 2002-10-21: [bug 642] bad normal calc) */
		factor = PointToPolygonFormFactor( pushedOrigin, trace->direction, light->w );
		if( factor == 0.0f )
			return qfalse;
		else if( factor < 0.0f )
		{
			if( light->flags & LIGHT_TWOSIDED )
				factor = -factor;
			else
				return qfalse;
		}
		
		/* ydnar: moved to here */
		add = factor * light->add;
	}
	
	/* point/spot lights */
	else if( light->type == EMIT_POINT || light->type == EMIT_SPOT )
	{
		/* clamp the distance to prevent super hot spots */
		if( dist < 16.0f )
			dist = 16.0f;
		
		/* attenuate */
		if( light->flags & LIGHT_ATTEN_LINEAR )
		{
			add = light->photons * linearScale - (dist * light->fade);
			if( add < 0.0f )
				add = 0.0f;
		}
		else
			add = light->photons / (dist * dist);
		
		/* handle spotlights */
		if( light->type == EMIT_SPOT )
		{
			float	distByNormal, radiusAtDist, sampleRadius;
			vec3_t	pointAtDist, distToSample;
			
			
			/* do cone calculation */
			distByNormal = -DotProduct( trace->displacement, light->normal );
			if( distByNormal < 0.0f )
				return qfalse;
			VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
			radiusAtDist = light->radiusByDist * distByNormal;
			VectorSubtract( trace->origin, pointAtDist, distToSample );
			sampleRadius = VectorLength( distToSample );
			
			/* outside the cone */
			if( sampleRadius >= radiusAtDist )
				return qfalse;
			
			/* attenuate */
			if( sampleRadius > (radiusAtDist - 32.0f) )
				add *= ((radiusAtDist - sampleRadius) / 32.0f);
		}
	}
	
	/* ydnar: sunlight */
	else if( light->type == EMIT_SUN )
	{
		/* attenuate */
		add = light->photons;
		if( add <= 0.0f )
			return qfalse;
		
		/* setup trace */
		trace->testAll = qtrue;
		VectorScale( light->color, add, trace->color );
		
		/* trace to point */
		if( trace->testOcclusion && !trace->forceSunlight )
		{
			/* trace */
			TraceLine( trace );
			if( !(trace->compileFlags & C_SKY) || trace->opaque )
			{
				VectorClear( trace->color );
				return -1;
			}
		}
		
		/* return to sender */
		return qtrue;
	}
	
	/* unknown light type */
	else
		return qfalse;
	
	/* ydnar: changed to a variable number */
	if( add <= 0.0f || (add <= light->falloffTolerance && (light->flags & LIGHT_FAST_ACTUAL)) )
		return qfalse;
	
	/* setup trace */
	trace->testAll = qfalse;
	VectorScale( light->color, add, trace->color );
	
	/* trace */
	TraceLine( trace );
	if( trace->passSolid )
	{
		VectorClear( trace->color );
		return qfalse;
	}
	
	/* we have a valid sample */
	return qtrue;
}



/*
TraceGrid()
grid samples are for quickly determining the lighting
of dynamically placed entities in the world
*/

#define	MAX_CONTRIBUTIONS	1024

typedef struct
{
	vec3_t		dir;
	vec3_t		color;
	int			style;
}
contribution_t;

void TraceGrid( int num )
{
	int						i, j, x, y, z, mod, step, numCon, numStyles;
	float					d;
	vec3_t					baseOrigin, cheapColor, color;
	rawGridPoint_t			*gp;
	bspGridPoint_t			*bgp;
	contribution_t			contributions[ MAX_CONTRIBUTIONS ];
	trace_t					trace;
	
	
	/* get grid points */
	gp = &rawGridPoints[ num ];
	bgp = &bspGridPoints[ num ];
	
	/* get grid origin */
	mod = num;
	z = mod / (gridBounds[ 0 ] * gridBounds[ 1 ]);
	mod -= z * (gridBounds[ 0 ] * gridBounds[ 1 ]);
	y = mod / gridBounds[ 0 ];
	mod -= y * gridBounds[ 0 ];
	x = mod;
	
	trace.origin[ 0 ] = gridMins[ 0 ] + x * gridSize[ 0 ];
	trace.origin[ 1 ] = gridMins[ 1 ] + y * gridSize[ 1 ];
	trace.origin[ 2 ] = gridMins[ 2 ] + z * gridSize[ 2 ];
	
	/* set inhibit sphere */
	if( gridSize[ 0 ] > gridSize[ 1 ] && gridSize[ 0 ] > gridSize[ 2 ] )
		trace.inhibitRadius = gridSize[ 0 ] * 0.5f;
	else if( gridSize[ 1 ] > gridSize[ 0 ] && gridSize[ 1 ] > gridSize[ 2 ] )
		trace.inhibitRadius = gridSize[ 1 ] * 0.5f;
	else
		trace.inhibitRadius = gridSize[ 2 ] * 0.5f;
	
	/* find point cluster */
	trace.cluster = ClusterForPointExt( trace.origin, GRID_EPSILON );
	if( trace.cluster < 0 )
	{
		/* try to nudge the origin around to find a valid point */
		VectorCopy( trace.origin, baseOrigin );
		for( step = 9; step <= 18; step += 9 )
		{
			for( i = 0; i < 8; i++ )
			{
				VectorCopy( baseOrigin, trace.origin );
				if( i & 1 )
					trace.origin[ 0 ] += step;
				else
					trace.origin[ 0 ] -= step;
				
				if( i & 2 )
					trace.origin[ 1 ] += step;
				else
					trace.origin[ 1 ] -= step;
				
				if( i & 4 )
					trace.origin[ 2 ] += step;
				else
					trace.origin[ 2 ] -= step;
				
				/* ydnar: changed to find cluster num */
				trace.cluster = ClusterForPointExt( trace.origin, VERTEX_EPSILON );
				if( trace.cluster >= 0 )
					break;
			}
			
			if( i != 8 )
				break;
		}
		
		/* can't find a valid point at all */
		if( step > 18 )
			return;
	}
	
	/* setup trace */
	trace.testOcclusion = !noTrace;
	trace.forceSunlight = qfalse;
	trace.recvShadows = WORLDSPAWN_RECV_SHADOWS;
	trace.numSurfaces = 0;
	trace.surfaces = NULL;
	trace.numLights = 0;
	trace.lights = NULL;
	
	/* clear */
	numCon = 0;
	VectorClear( cheapColor );
	
	/* trace to all the lights, find the major light direction, and divide the
	   total light between that along the direction and the remaining in the ambient */
	for( trace.light = lights; trace.light != NULL; trace.light = trace.light->next )
	{
		float		addSize;
		
		
		/* sample light */
		if( !LightContributionToPoint( &trace ) )
			continue;
		
		/* handle negative light */
		if( trace.light->flags & LIGHT_NEGATIVE )
			VectorScale( trace.color, -1.0f, trace.color );
		
		/* add a contribution */
		VectorCopy( trace.color, contributions[ numCon ].color );
		VectorCopy( trace.direction, contributions[ numCon ].dir );
		contributions[ numCon ].style = trace.light->style;
		numCon++;
		
		/* push average direction around */
		addSize = VectorLength( trace.color );
		VectorMA( gp->dir, addSize, trace.direction, gp->dir );
		
		/* stop after a while */
		if( numCon >= (MAX_CONTRIBUTIONS - 1) )
			break;
		
		/* ydnar: cheap mode */
		VectorAdd( cheapColor, trace.color, cheapColor );
		if( cheapgrid && cheapColor[ 0 ] >= 255.0f && cheapColor[ 1 ] >= 255.0f && cheapColor[ 2 ] >= 255.0f )
			break;
	}
	
	/////// Floodlighting for point //////////////////
	//do our floodlight ambient occlusion loop, and add a single contribution based on the brightest dir
	if (floodlighty)
	{
		int q;
		float addSize,f;
		vec3_t col,dir;
		col[0]=col[1]=col[2]=floodlightIntensity;
		dir[0]=dir[1]=0;
		dir[2]=1;
      
		trace.testOcclusion = qtrue;
		trace.forceSunlight = qfalse;
		trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
		trace.testAll = qtrue;     
      
		for (q=0;q<6;q++)
		{
			switch(q)
			{
			case 0: //z+
				trace.normal[0]=0;
				trace.normal[1]=0;
				trace.normal[2]=1;
			break;
			case 1://z-
				trace.normal[0]=0;
				trace.normal[1]=0;
				trace.normal[2]=-1;
			break;
			case 2: //x-
				trace.normal[0]=-1;
				trace.normal[1]=0;
				trace.normal[2]=0;
			break;
			case 3: //x+
				trace.normal[0]=1;
				trace.normal[1]=0;
				trace.normal[2]=0;
			break;
			case 4: //y-
				trace.normal[0]=0;
				trace.normal[1]=-1;
				trace.normal[2]=0;
			break;
			case 5: //y+
				trace.normal[0]=0;
				trace.normal[1]=1;
				trace.normal[2]=0;
			break;
			}

			f = FloodLightForSample(&trace);

			contributions[ numCon ].color[0]=col[0]*f;
			contributions[ numCon ].color[1]=col[1]*f;
			contributions[ numCon ].color[2]=col[2]*f;
			

			contributions[ numCon ].dir[0]=dir[0];
			contributions[ numCon ].dir[1]=dir[1];
			contributions[ numCon ].dir[2]=dir[2];

			contributions[ numCon ].style = 0;
			numCon++;		
			/* push average direction around */
			//addSize = VectorLength( col );
			//VectorMA( gp->dir, addSize, dir, gp->dir );

			
		}
		if (VectorLength(gp->dir)<0.1)
		{
			gp->dir[0]=0;
			gp->dir[1]=0;
			gp->dir[2]=-1;
		}
	}
	/////////////////////

	/* normalize to get primary light direction */
	VectorNormalize( gp->dir, gp->dir );
	
	/* now that we have identified the primary light direction,
	   go back and separate all the light into directed and ambient */
	numStyles = 1;
	for( i = 0; i < numCon; i++ )
	{
		/* get relative directed strength */
		d = DotProduct( contributions[ i ].dir, gp->dir );
		if( d < 0.0f )
			d = 0.0f;
		
		/* find appropriate style */
		for( j = 0; j < numStyles; j++ )
		{
			if( gp->styles[ j ] == contributions[ i ].style )
				break;
		}
		
		/* style not found? */
		if( j >= numStyles )
		{
			/* add a new style */
			if( numStyles < MAX_LIGHTMAPS )
			{
				gp->styles[ numStyles ] = contributions[ i ].style;
				bgp->styles[ numStyles ] = contributions[ i ].style;
				numStyles++;
				//%	Sys_Printf( "(%d, %d) ", num, contributions[ i ].style );
			}
			
			/* fallback */
			else
				j = 0;
		}
		
		/* add the directed color */
		VectorMA( gp->directed[ j ], d, contributions[ i ].color, gp->directed[ j ] );
		
		/* ambient light will be at 1/4 the value of directed light */
		/* (ydnar: nuke this in favor of more dramatic lighting?) */
		/* (PM: how about actually making it work? d=1 when it got here for single lights/sun :P */
//		d = 0.25f;
		/* (Hobbes: always setting it to .25 is hardly any better) */
		d = 0.25f * (1.0f - d);
		VectorMA( gp->ambient[ j ], d, contributions[ i ].color, gp->ambient[ j ] );
	}
	
	
	/* store off sample */
	for( i = 0; i < MAX_LIGHTMAPS; i++ )
	{
		/* do some fudging to keep the ambient from being too low (2003-07-05: 0.25 -> 0.125) */
		if( !bouncing )
			VectorMA( gp->ambient[ i ], 0.125f, gp->directed[ i ], gp->ambient[ i ] );
		
		/* set minimum light and copy off to bytes */
		VectorCopy( gp->ambient[ i ], color );
		for( j = 0; j < 3; j++ )
			if( color[ j ] < minGridLight[ j ] )
				color[ j ] = minGridLight[ j ];
		ColorToBytes( color, bgp->ambient[ i ], 1.0f );
		ColorToBytes( gp->directed[ i ], bgp->directed[ i ], 1.0f );
	}
	
	/* debug code */
	#if 0
		//%	Sys_FPrintf( SYS_VRB, "%10d %10d %10d ", &gp->ambient[ 0 ][ 0 ], &gp->ambient[ 0 ][ 1 ], &gp->ambient[ 0 ][ 2 ] );
		Sys_FPrintf( SYS_VRB, "%9d Amb: (%03.1f %03.1f %03.1f) Dir: (%03.1f %03.1f %03.1f)\n",
			num,
			gp->ambient[ 0 ][ 0 ], gp->ambient[ 0 ][ 1 ], gp->ambient[ 0 ][ 2 ],
			gp->directed[ 0 ][ 0 ], gp->directed[ 0 ][ 1 ], gp->directed[ 0 ][ 2 ] );
	#endif
	
	/* store direction */
	if( !bouncing )
		NormalToLatLong( gp->dir, bgp->latLong );
}



/*
SetupGrid()
calculates the size of the lightgrid and allocates memory
*/

void SetupGrid( void )
{
	int			i, j;
	vec3_t		maxs, oldGridSize;
	const char	*value;
	char		temp[ 64 ];
	
	 
	/* don't do this if not grid lighting */
	if( noGridLighting )
		return;
	
	/* ydnar: set grid size */
	value = ValueForKey( &entities[ 0 ], "gridsize" );
	if( value[ 0 ] != '\0' )
		sscanf( value, "%f %f %f", &gridSize[ 0 ], &gridSize[ 1 ], &gridSize[ 2 ] );
	
	/* quantize it */
	VectorCopy( gridSize, oldGridSize );
	for( i = 0; i < 3; i++ )
		gridSize[ i ] = gridSize[ i ] >= 8.0f ? floor( gridSize[ i ] ) : 8.0f;
	
	/* ydnar: increase gridSize until grid count is smaller than max allowed */
	numRawGridPoints = MAX_MAP_LIGHTGRID + 1;
	j = 0;
	while( numRawGridPoints > MAX_MAP_LIGHTGRID )
	{
		/* get world bounds */
		for( i = 0; i < 3; i++ )
		{
			gridMins[ i ] = gridSize[ i ] * ceil( bspModels[ 0 ].mins[ i ] / gridSize[ i ] );
			maxs[ i ] = gridSize[ i ] * floor( bspModels[ 0 ].maxs[ i ] / gridSize[ i ] );
			gridBounds[ i ] = (maxs[ i ] - gridMins[ i ]) / gridSize[ i ] + 1;
		}
	
		/* set grid size */
		numRawGridPoints = gridBounds[ 0 ] * gridBounds[ 1 ] * gridBounds[ 2 ];
		
		/* increase grid size a bit */
		if( numRawGridPoints > MAX_MAP_LIGHTGRID )
			gridSize[ j++ % 3 ] += 16.0f;
	}
	
	/* print it */
	Sys_Printf( "Grid size = { %1.0f, %1.0f, %1.0f }\n", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );
	
	/* different? */
	if( !VectorCompare( gridSize, oldGridSize ) )
	{
		sprintf( temp, "%.0f %.0f %.0f", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );
		SetKeyValue( &entities[ 0 ], "gridsize", (const char*) temp );
		Sys_FPrintf( SYS_VRB, "Storing adjusted grid size\n" );
	}
	
	/* 2nd variable. fixme: is this silly? */
	numBSPGridPoints = numRawGridPoints;
	
	/* allocate lightgrid */
	rawGridPoints = safe_malloc( numRawGridPoints * sizeof( *rawGridPoints ) );
	memset( rawGridPoints, 0, numRawGridPoints * sizeof( *rawGridPoints ) );
	
	if( bspGridPoints != NULL )
		free( bspGridPoints );
	bspGridPoints = safe_malloc( numBSPGridPoints * sizeof( *bspGridPoints ) );
	memset( bspGridPoints, 0, numBSPGridPoints * sizeof( *bspGridPoints ) );
	
	/* clear lightgrid */
	for( i = 0; i < numRawGridPoints; i++ )
	{
		VectorCopy( ambientColor, rawGridPoints[ i ].ambient[ j ] );
		rawGridPoints[ i ].styles[ 0 ] = LS_NORMAL;
		bspGridPoints[ i ].styles[ 0 ] = LS_NORMAL;
		for( j = 1; j < MAX_LIGHTMAPS; j++ )
		{
			rawGridPoints[ i ].styles[ j ] = LS_NONE;
			bspGridPoints[ i ].styles[ j ] = LS_NONE;
		}
	}
	
	/* note it */
	Sys_Printf( "%9d grid points\n", numRawGridPoints );
}


void WriteRadbumpChunk()
{
	FILE	*file;
	char				dirname[ 1024 ], filename[ 1024 ];
	int size;	

	if (!radbump) return;
	
	/* note it */
	Sys_Printf( "--- WriteRadbumpChunk ---\n");
	
	strcpy( dirname, source );
	StripExtension( dirname );

	//Check to see if the chunk exists
	sprintf( filename, "%s.rad", dirname );

	file = fopen( filename, "wb" );

	
	fwrite( radfilebuffer,1,radbumpsize,file);
	fclose(file);


}


/*
LightWorld()
does what it says...
*/

void LightWorld( void )
{
	vec3_t		color;
	float		f;
	int			b, bt;
	int j;
	int radbumppassmax=0;
	int radbounce;
	int i;
	qboolean	minVertex, minGrid;
	const char	*value;
	bspDrawSurface_t	*ds;

	/* ydnar: smooth normals */
	if( shade )
	{
		Sys_Printf( "--- SmoothNormals ---\n" );
		SmoothNormals();
	}

	/* determine the number of grid points */
	Sys_Printf( "--- SetupGrid ---\n" );
	SetupGrid();
	
	/* find the optional minimum lighting values */
	GetVectorForKey( &entities[ 0 ], "_color", color );
	if( VectorLength( color ) == 0.0f )
		VectorSet( color, 1.0, 1.0, 1.0 );
	
	/* ambient */
	f = FloatForKey( &entities[ 0 ], "_ambient" );
	if( f == 0.0f )
		f = FloatForKey( &entities[ 0 ], "ambient" );
	VectorScale( color, f, ambientColor );
	
	/* minvertexlight */
	minVertex = qfalse;
	value = ValueForKey( &entities[ 0 ], "_minvertexlight" );
	if( value[ 0 ] != '\0' )
	{
		minVertex = qtrue;
		f = atof( value );
		VectorScale( color, f, minVertexLight );
	}
	
	/* mingridlight */
	minGrid = qfalse;
	value = ValueForKey( &entities[ 0 ], "_mingridlight" );
	if( value[ 0 ] != '\0' )
	{
		minGrid = qtrue;
		f = atof( value );
		VectorScale( color, f, minGridLight );
	}
	
	/* minlight */
	value = ValueForKey( &entities[ 0 ], "_minlight" );
	if( value[ 0 ] != '\0' )
	{
		f = atof( value );
		VectorScale( color, f, minLight );
		if( minVertex == qfalse )
			VectorScale( color, f, minVertexLight );
		if( minGrid == qfalse )
			VectorScale( color, f, minGridLight );
	}
	
	
	//Radbump setup?
	radbounce = bounce;
	radbumppassmax=1;
	if (radbump==qtrue) radbumppassmax=4;

	//record sun/skylight settings for later
		

	//RAD BUMPING
	for (j=0;j<radbumppassmax;j++)
	{
		radbumppass=j;
		bounce = radbounce;
		Sys_Printf( "--- RADBUMP PASS %d of 4 ---\n", j+1  );


		//clear lightmaps
		for (i=0;i<numRawLightmaps;i++)
		{
			ClearLightMap( &rawLightmaps[ i ]);
		}

		
		for( i = 0; i < MAX_LIGHTMAPS; i++ )
		{
			memset( vertexLuxels[ i ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
			memset( radVertexLuxels[ i ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
		}

		//reset some counters
		numLuxelsMapped=0;
		numLuxels=0;
		numLuxelsOccluded=0;
		numVertsIlluminated=0;
		numLuxelsIlluminated=0;

		numPointLights=0;
		numSpotLights=0;
		numSunLights=0;
		numAreaLights=0;
		numDiffuseLights=0;

		//Delete any existing lights
		RadFreeLights();

		/* create world lights */
		Sys_FPrintf( SYS_VRB, "--- CreateLights ---\n" );
		CreateEntityLights();
		CreateSurfaceLights();
		Sys_Printf( "%9d point lights\n", numPointLights );
		Sys_Printf( "%9d spotlights\n", numSpotLights );
		Sys_Printf( "%9d diffuse (area) lights\n", numDiffuseLights );
		Sys_Printf( "%9d sun/sky lights\n", numSunLights );
		
		/* calculate lightgrid */
		if( !noGridLighting )
		{
			/* ydnar: set up light envelopes */
			SetupEnvelopes( qtrue, fastgrid );
			
			Sys_Printf( "--- TraceGrid ---\n" );
			RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );
			Sys_Printf( "%d x %d x %d = %d grid\n",
				gridBounds[ 0 ], gridBounds[ 1 ], gridBounds[ 2 ], numBSPGridPoints );
			
			/* ydnar: emit statistics on light culling */
			Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );
			Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );
		}
		
		/* slight optimization to remove a sqrt */
		subdivideThreshold *= subdivideThreshold;


		/* map the world luxels */
		Sys_Printf( "--- MapRawLightmap ---\n" );
		RunThreadsOnIndividual( numRawLightmaps, qtrue, MapRawLightmap );
		Sys_Printf( "%9d luxels\n", numLuxels );
		Sys_Printf( "%9d luxels mapped\n", numLuxelsMapped );
		Sys_Printf( "%9d luxels occluded\n", numLuxelsOccluded );
	


		/* dirty them up */
		if( dirty )
		{
			Sys_Printf( "--- DirtyRawLightmap ---\n" );
			RunThreadsOnIndividual( numRawLightmaps, qtrue, DirtyRawLightmap );
		}
		
		/* floodlight them up */
		if( floodlighty )
		{
			Sys_Printf( "--- FloodlightRawLightmap ---\n" );
			RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
		}

		/* ydnar: set up light envelopes */
		SetupEnvelopes( qfalse, fast );
		
		/* light up my world */
		lightsPlaneCulled = 0;
		lightsEnvelopeCulled = 0;
		lightsBoundsCulled = 0;
		lightsClusterCulled = 0;
		
		Sys_Printf( "--- IlluminateRawLightmap ---\n" );
		RunThreadsOnIndividual( numRawLightmaps, qtrue, IlluminateRawLightmap );
		Sys_Printf( "%9d luxels illuminated\n", numLuxelsIlluminated );
		
		StitchSurfaceLightmaps();
		
		Sys_Printf( "--- IlluminateVertexes ---\n" );
		RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, IlluminateVertexes );
		Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
		
		/* ydnar: emit statistics on light culling */
		Sys_FPrintf( SYS_VRB, "%9d lights plane culled\n", lightsPlaneCulled );
		Sys_FPrintf( SYS_VRB, "%9d lights envelope culled\n", lightsEnvelopeCulled );
		Sys_FPrintf( SYS_VRB, "%9d lights bounds culled\n", lightsBoundsCulled );
		Sys_FPrintf( SYS_VRB, "%9d lights cluster culled\n", lightsClusterCulled );


		if (radbump)
		{
			StoreSurfaceLightmaps();
			Sys_Printf( "Writing %s\n", source );
			WriteBSPFile( source );
		}

		b = 1;
		bt = bounce;
		while( bounce > 0 )
		{
			/* store off the bsp between bounces */
			StoreSurfaceLightmaps();
			Sys_Printf( "Writing %s\n", source );
			WriteBSPFile( source );
			
			/* note it */
			Sys_Printf( "\n--- Radiosity (bounce %d of %d) ---\n", b, bt );
			
			/* flag bouncing */
			bouncing = qtrue;
			VectorClear( ambientColor );
			
			/* generate diffuse lights */
			RadFreeLights();
			RadCreateDiffuseLights();
			
			/* setup light envelopes */
			SetupEnvelopes( qfalse, fastbounce );
			if( numLights == 0 )
			{
				Sys_Printf( "No diffuse light to calculate, ending radiosity.\n" );
				break;
			}
			
			/* add to lightgrid */
			if( bouncegrid )
			{
				gridEnvelopeCulled = 0;
				gridBoundsCulled = 0;
				
				Sys_Printf( "--- BounceGrid ---\n" );
				RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );
				Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );
				Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );
			}
			
			/* light up my world */
			lightsPlaneCulled = 0;
			lightsEnvelopeCulled = 0;
			lightsBoundsCulled = 0;
			lightsClusterCulled = 0;
			
			Sys_Printf( "--- IlluminateRawLightmap ---\n" );
			RunThreadsOnIndividual( numRawLightmaps, qtrue, IlluminateRawLightmap );
			Sys_Printf( "%9d luxels illuminated\n", numLuxelsIlluminated );
			Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
			
			StitchSurfaceLightmaps();
			
			Sys_Printf( "--- IlluminateVertexes ---\n" );
			RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, IlluminateVertexes );
			Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
			
			/* ydnar: emit statistics on light culling */
			Sys_FPrintf( SYS_VRB, "%9d lights plane culled\n", lightsPlaneCulled );
			Sys_FPrintf( SYS_VRB, "%9d lights envelope culled\n", lightsEnvelopeCulled );
			Sys_FPrintf( SYS_VRB, "%9d lights bounds culled\n", lightsBoundsCulled );
			Sys_FPrintf( SYS_VRB, "%9d lights cluster culled\n", lightsClusterCulled );
			
			/* interate */
			bounce--;
			b++;
		}
		bouncing=qfalse;
	}
}



/*
LightMain()
main routine for light processing
*/

int LightMain( int argc, char **argv )
{
	int			i;
	float		f;
	char		mapSource[ 1024 ];
	const char	*value;
	
	
	/* note it */
	Sys_Printf( "--- Light ---\n" );
	
	/* set standard game flags */
	wolfLight = game->wolfLight;
	lmCustomSize = game->lightmapSize;
	lightmapGamma = game->lightmapGamma;
	lightmapCompensate = game->lightmapCompensate;
	
	/* process commandline arguments */
	for( i = 1; i < (argc - 1); i++ )
	{
		/* lightsource scaling */
		if( !strcmp( argv[ i ], "-point" ) || !strcmp( argv[ i ], "-pointscale" ) )
		{
			f = atof( argv[ i + 1 ] );
			pointScale *= f;
			Sys_Printf( "Point (entity) light scaled by %f to %f\n", f, pointScale );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-area" ) || !strcmp( argv[ i ], "-areascale" ) )
		{
			f = atof( argv[ i + 1 ] );
			areaScale *= f;
			Sys_Printf( "Area (shader) light scaled by %f to %f\n", f, areaScale );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-sky" ) || !strcmp( argv[ i ], "-skyscale" ) )
		{
			f = atof( argv[ i + 1 ] );
			skyScale *= f;
			Sys_Printf( "Sky/sun light scaled by %f to %f\n", f, skyScale );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-bouncescale" ) )
		{
			f = atof( argv[ i + 1 ] );
			bounceScale *= f;
			Sys_Printf( "Bounce (radiosity) light scaled by %f to %f\n", f, bounceScale );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-scale" ) )
		{
			f = atof( argv[ i + 1 ] );
			pointScale *= f;
			areaScale *= f;
			skyScale *= f;
			bounceScale *= f;
			Sys_Printf( "All light scaled by %f\n", f );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-gamma" ) )
		{
			f = atof( argv[ i + 1 ] );
			lightmapGamma = f;
			Sys_Printf( "Lighting gamma set to %f\n", lightmapGamma );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-exposure" ) )
		{
			f = atof( argv[ i + 1 ] );
			lightmapExposure = f;
			Sys_Printf( "Lighting exposure set to %f\n", lightmapExposure );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-compensate" ) )
		{
			f = atof( argv[ i + 1 ] );
			if( f <= 0.0f )
				f = 1.0f;
			lightmapCompensate = f;
			Sys_Printf( "Lighting compensation set to 1/%f\n", lightmapCompensate );
			i++;
		}
		
		/* ydnar switches */
		else if( !strcmp( argv[ i ], "-bounce" ) )
		{
			bounce = atoi( argv[ i + 1 ] );
			if( bounce < 0 )
				bounce = 0;
			else if( bounce > 0 )
				Sys_Printf( "Radiosity enabled with %d bounce(s)\n", bounce );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-supersample" ) || !strcmp( argv[ i ], "-super" ) )
		{
			superSample = atoi( argv[ i + 1 ] );
			if( superSample < 1 )
				superSample = 1;
			else if( superSample > 1 )
				Sys_Printf( "Ordered-grid supersampling enabled with %d sample(s) per lightmap texel\n", (superSample * superSample) );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-samples" ) )
		{
			lightSamples = atoi( argv[ i + 1 ] );
			if( lightSamples < 1 )
				lightSamples = 1;
			else if( lightSamples > 1 )
				Sys_Printf( "Adaptive supersampling enabled with %d sample(s) per lightmap texel\n", lightSamples );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-filter" ) )
		{
			filter = qtrue;
			Sys_Printf( "Lightmap filtering enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-dark" ) )
		{
			dark = qtrue;
			Sys_Printf( "Dark lightmap seams enabled\n" );
		}
		






		else if( !strcmp( argv[ i ], "-shadeangle" ) )
		{
			shadeAngleDegrees = atof( argv[ i + 1 ] );
			if( shadeAngleDegrees < 0.0f )
				shadeAngleDegrees = 0.0f;
			else if( shadeAngleDegrees > 0.0f )
			{
				shade = qtrue;
				Sys_Printf( "Phong shading enabled with a breaking angle of %f degrees\n", shadeAngleDegrees );
			}
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-thresh" ) )
		{
			subdivideThreshold = atof( argv[ i + 1 ] );
			if( subdivideThreshold < 0 )
				subdivideThreshold = DEFAULT_SUBDIVIDE_THRESHOLD;
			else
				Sys_Printf( "Subdivision threshold set at %.3f\n", subdivideThreshold );
			i++;
		}
		
		else if( !strcmp( argv[ i ], "-approx" ) )
		{
			approximateTolerance = atoi( argv[ i + 1 ] );
			if( approximateTolerance < 0 )
				approximateTolerance = 0;
			else if( approximateTolerance > 0 )
				Sys_Printf( "Approximating lightmaps within a byte tolerance of %d\n", approximateTolerance );
			i++;
		}
		
		
		else if( !strcmp( argv[ i ], "-radbump" ) )
		{
			radbump = qtrue;
			deluxemap = qtrue;
			noCollapse = qtrue; 
			radfilebuffer=NULL;

			Sys_Printf( "Generating radbump - this is going to take a while.\n" );
		}
		else if( !strcmp( argv[ i ], "-radbumpdebug" ) )
		{
			radbump = qtrue;
			radbumpdebug =qtrue;
			noCollapse = qtrue;
			deluxemap = qtrue;
			radfilebuffer=NULL;

			Sys_Printf( "Generating radbump - this is going to take a while.\n" );
		}
		
		else if( !strcmp( argv[ i ], "-external" ) )
		{
			externalLightmaps = qtrue;
			Sys_Printf( "Storing all lightmaps externally\n" );
		}

		else if( !strcmp( argv[ i ], "-lightmapsize" ) )
		{
			lmCustomSize = atoi( argv[ i + 1 ] );
			
			/* must be a power of 2 and greater than 2 */
			if( ((lmCustomSize - 1) & lmCustomSize) || lmCustomSize < 2 )
			{
				Sys_Printf( "WARNING: Lightmap size must be a power of 2, greater or equal to 2 pixels.\n" );
				lmCustomSize = game->lightmapSize;
			}
			i++;
			Sys_Printf( "Default lightmap size set to %d x %d pixels\n", lmCustomSize, lmCustomSize );
			
			/* enable external lightmaps */
			if( lmCustomSize != game->lightmapSize )
			{
				externalLightmaps = qtrue;
				Sys_Printf( "Storing all lightmaps externally\n" );
			}
		}
		
		/* ydnar: add this to suppress warnings */
		else if( !strcmp( argv[ i ],  "-custinfoparms") )
		{
			Sys_Printf( "Custom info parms enabled\n" );
			useCustomInfoParms = qtrue;
		}
		
		else if( !strcmp( argv[ i ], "-wolf" ) )
		{
			/* -game should already be set */
			wolfLight = qtrue;
			Sys_Printf( "Enabling Wolf lighting model (linear default)\n" );
		}
		
		else if( !strcmp( argv[ i ], "-q3" ) )
		{
			/* -game should already be set */
			wolfLight = qfalse;
			Sys_Printf( "Enabling Quake 3 lighting model (nonlinear default)\n" );
		}
		
		else if( !strcmp( argv[ i ], "-sunonly" ) )
		{
			sunOnly = qtrue;
			Sys_Printf( "Only computing sunlight\n" );
		}
		
		else if( !strcmp( argv[ i ], "-bounceonly" ) )
		{
			bounceOnly = qtrue;
			Sys_Printf( "Storing bounced light (radiosity) only\n" );
		}
		
		else if( !strcmp( argv[ i ], "-nocollapse" ) )
		{
			noCollapse = qtrue;
			Sys_Printf( "Identical lightmap collapsing disabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-shade" ) )
		{
			shade = qtrue;
			Sys_Printf( "Phong shading enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-bouncegrid") )
		{
			bouncegrid = qtrue;
			if( bounce > 0 )
				Sys_Printf( "Grid lighting with radiosity enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-smooth" ) )
		{
			lightSamples = EXTRA_SCALE;
			Sys_Printf( "The -smooth argument is deprecated, use \"-samples 2\" instead\n" );
		}
		
		else if( !strcmp( argv[ i ], "-fast" ) )
		{
			fast = qtrue;
			fastgrid = qtrue;
			fastbounce = qtrue;
			Sys_Printf( "Fast mode enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-faster" ) )
		{
			faster = qtrue;
			fast = qtrue;
			fastgrid = qtrue;
			fastbounce = qtrue;
			Sys_Printf( "Faster mode enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-fastgrid" ) )
		{
			fastgrid = qtrue;
			Sys_Printf( "Fast grid lighting enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-fastbounce" ) )
		{
			fastbounce = qtrue;
			Sys_Printf( "Fast bounce mode enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-cheap" ) )
		{
			cheap = qtrue;
			cheapgrid = qtrue;
			Sys_Printf( "Cheap mode enabled\n" );
		}

		else if( !strcmp( argv[ i ], "-cheapgrid" ) )
		{
			cheapgrid = qtrue;
			Sys_Printf( "Cheap grid mode enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-normalmap" ) )
		{
			normalmap = qtrue;
			Sys_Printf( "Storing normal map instead of lightmap\n" );
		}
		
		else if( !strcmp( argv[ i ], "-trisoup" ) )
		{
			trisoup = qtrue;
			Sys_Printf( "Converting brush faces to triangle soup\n" );
		}
		
		else if( !strcmp( argv[ i ], "-debug" ) )
		{
			debug = qtrue;
			Sys_Printf( "Lightmap debugging enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-debugsurfaces" ) || !strcmp( argv[ i ], "-debugsurface" ) )
		{
			debugSurfaces = qtrue;
			Sys_Printf( "Lightmap surface debugging enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-debugunused" ) )
		{
			debugUnused = qtrue;
			Sys_Printf( "Unused luxel debugging enabled\n" );
		}

		else if( !strcmp( argv[ i ], "-debugaxis" ) )
		{
			debugAxis = qtrue;
			Sys_Printf( "Lightmap axis debugging enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-debugcluster" ) )
		{
			debugCluster = qtrue;
			Sys_Printf( "Luxel cluster debugging enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-debugorigin" ) )
		{
			debugOrigin = qtrue;
			Sys_Printf( "Luxel origin debugging enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-debugdeluxe" ) )
		{
			deluxemap = qtrue;
			debugDeluxemap = qtrue;
			Sys_Printf( "Deluxemap debugging enabled\n" );
		}
		
		else if( !strcmp( argv[ i ], "-export" ) )
		{
			exportLightmaps = qtrue;
			Sys_Printf( "Exporting lightmaps\n" );
		}
		
		else if( !strcmp(argv[ i ], "-notrace" )) 
		{
			noTrace = qtrue;
			Sys_Printf( "Shadow occlusion disabled\n" );
		}
		else if( !strcmp(argv[ i ], "-patchshadows" ) )
		{
			patchShadows = qtrue;
			Sys_Printf( "Patch shadow casting enabled\n" );
		}
		else if( !strcmp( argv[ i ], "-extra" ) )
		{
			superSample = EXTRA_SCALE;		/* ydnar */
			Sys_Printf( "The -extra argument is deprecated, use \"-super 2\" instead\n" );
		}
		else if( !strcmp( argv[ i ], "-extrawide" ) )
		{
			superSample = EXTRAWIDE_SCALE;	/* ydnar */
			filter = qtrue;					/* ydnar */
			Sys_Printf( "The -extrawide argument is deprecated, use \"-filter [-super 2]\" instead\n");
		}
		else if( !strcmp( argv[ i ], "-samplesize" ) )
		{
			sampleSize = atoi( argv[ i + 1 ] );
			if( sampleSize < 1 )
				sampleSize = 1;
			i++;
			Sys_Printf( "Default lightmap sample size set to %dx%d units\n", sampleSize, sampleSize );
		}
		else if( !strcmp( argv[ i ], "-novertex" ) )
		{
			noVertexLighting = qtrue;
			Sys_Printf( "Disabling vertex lighting\n" );
		}
		else if( !strcmp( argv[ i ], "-nogrid" ) )
		{
			noGridLighting = qtrue;
			Sys_Printf( "Disabling grid lighting\n" );
		}
		else if( !strcmp( argv[ i ], "-border" ) )
		{
			lightmapBorder = qtrue;
			Sys_Printf( "Adding debug border to lightmaps\n" );
		}
		else if( !strcmp( argv[ i ], "-nosurf" ) )
		{
			noSurfaces = qtrue;
			Sys_Printf( "Not tracing against surfaces\n" );
		}
		else if( !strcmp( argv[ i ], "-dump" ) )
		{
			dump = qtrue;
			Sys_Printf( "Dumping radiosity lights into numbered prefabs\n" );
		}
		else if( !strcmp( argv[ i ], "-lomem" ) )
		{
			loMem = qtrue;
			Sys_Printf( "Enabling low-memory (potentially slower) lighting mode\n" );
		}
		
		else if( !strcmp( argv[ i ], "-cpma" ) )
		{
			cpmaHack = qtrue;
			Sys_Printf( "Enabling Challenge Pro Mode Asstacular Vertex Lighting Mode (tm)\n" );
		}
		else if( !strcmp( argv[ i ], "-floodlight" ) )
		{
			floodlighty = qtrue;
			Sys_Printf( "FloodLighting enabled\n" );
		}
		else if( !strcmp( argv[ i ], "-noboundstest" ) )
		{
			noboundstest = qtrue;
			Sys_Printf( "No Bounds Test enabled\n" );
		}
		else if( !strcmp( argv[ i ], "-debugnormals" ) )
		{
			debugnormals = qtrue;
			Sys_Printf( "DebugNormals enabled\n" );
		}
		else if( !strcmp( argv[ i ], "-lowquality" ) )
		{
			floodlight_lowquality = qtrue;
			Sys_Printf( "Low Quality FloodLighting enabled\n" );
		}
		
		/* r7: dirtmapping */
		else if( !strcmp( argv[ i ], "-dirty" ) )
		{
			dirty = qtrue;
			Sys_Printf( "Dirtmapping enabled\n" );
		}
		else if( !strcmp( argv[ i ], "-dirtdebug" ) || !strcmp( argv[ i ], "-debugdirt" ) )
		{
			dirtDebug = qtrue;
			Sys_Printf( "Dirtmap debugging enabled\n" );
		}
		else if( !strcmp( argv[ i ], "-dirtmode" ) )
		{
			dirtMode = atoi( argv[ i + 1 ] );
			if( dirtMode != 0 && dirtMode != 1 )
				dirtMode = 0;
			if( dirtMode == 1 )
				Sys_Printf( "Enabling randomized dirtmapping\n" );
			else
				Sys_Printf( "Enabling ordered dir mapping\n" );
		}
		else if( !strcmp( argv[ i ], "-dirtdepth" ) )
		{
			dirtDepth = atof( argv[ i + 1 ] );
			if( dirtDepth <= 0.0f )
				dirtDepth = 128.0f;
			Sys_Printf( "Dirtmapping depth set to %.1f\n", dirtDepth );
		}
		else if( !strcmp( argv[ i ], "-dirtscale" ) )
		{
			dirtScale = atof( argv[ i + 1 ] );
			if( dirtScale <= 0.0f )
				dirtScale = 1.0f;
			Sys_Printf( "Dirtmapping scale set to %.1f\n", dirtScale );
		}
		else if( !strcmp( argv[ i ], "-dirtgain" ) )
		{
			dirtGain = atof( argv[ i + 1 ] );
			if( dirtGain <= 0.0f )
				dirtGain = 1.0f;
			Sys_Printf( "Dirtmapping gain set to %.1f\n", dirtGain );
		}
		
		/* unhandled args */
		else
			Sys_Printf( "WARNING: Unknown argument \"%s\"\n", argv[ i ] );

	}
	
	/* clean up map name */
	strcpy( source, ExpandArg( argv[ i ] ) );
	StripExtension( source );
	DefaultExtension( source, ".bsp" );
	strcpy( mapSource, ExpandArg( argv[ i ] ) );
	StripExtension( mapSource );
	DefaultExtension( mapSource, ".map" );
	
	/* ydnar: set default sample size */
	SetDefaultSampleSize( sampleSize );
	
	/* ydnar: handle shaders */
	BeginMapShaderFile( source );
	LoadShaderInfo();
	
	/* note loading */
	Sys_Printf( "Loading %s\n", source );
	
	/* ydnar: load surface file */
	LoadSurfaceExtraFile( source );
	
	/* load bsp file */
	LoadBSPFile( source );
	
	/* parse bsp entities */
	ParseEntities();
	
	/* load map file */
	value = ValueForKey( &entities[ 0 ], "_keepLights" );
	if( value[ 0 ] != '1' )
		LoadMapFile( mapSource, qtrue );
	
	/* set the entity/model origins and init yDrawVerts */
	SetEntityOrigins();
	
	/* ydnar: set up optimization */
	SetupBrushes();
	SetupDirt();
	SetupFloodLight();
	SetupSurfaceLightmaps();
	
	/* initialize the surface facet tracing */
	SetupTraceNodes();
	
	/* light the world */
	LightWorld();
	
	/* ydnar: store off lightmaps */
	StoreSurfaceLightmaps();
	
	/*write out the radbump chunk*/
	WriteRadbumpChunk();

	/* write out the bsp */
	UnparseEntities();
	Sys_Printf( "Writing %s\n", source );
	WriteBSPFile( source );
	
	/* ydnar: export lightmaps */
	if( exportLightmaps && !externalLightmaps )
		ExportLightmaps();
	
	/* return to sender */
	return 0;
}

